/*
 * Copyright © 2004-2021 L2J Server
 * 
 * This file is part of L2J Server.
 * 
 * L2J Server is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * L2J Server is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.l2jserver.gameserver.model.zone.type;

import static com.l2jserver.gameserver.config.Configuration.castle;

import com.l2jserver.gameserver.datatables.SkillData;
import com.l2jserver.gameserver.enums.MountType;
import com.l2jserver.gameserver.instancemanager.ClanHallSiegeManager;
import com.l2jserver.gameserver.instancemanager.FortManager;
import com.l2jserver.gameserver.instancemanager.FortSiegeManager;
import com.l2jserver.gameserver.instancemanager.ZoneManager;
import com.l2jserver.gameserver.model.TeleportWhereType;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.entity.Fort;
import com.l2jserver.gameserver.model.entity.FortSiege;
import com.l2jserver.gameserver.model.entity.Siegable;
import com.l2jserver.gameserver.model.entity.clanhall.SiegableHall;
import com.l2jserver.gameserver.model.skills.BuffInfo;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.model.zone.AbstractZoneSettings;
import com.l2jserver.gameserver.model.zone.L2ZoneType;
import com.l2jserver.gameserver.model.zone.ZoneId;
import com.l2jserver.gameserver.network.SystemMessageId;

/**
 * A siege zone
 * @author durgus
 */
public class L2SiegeZone extends L2ZoneType {
	private static final int DISMOUNT_DELAY = 5;
	
	public L2SiegeZone(int id) {
		super(id);
		AbstractZoneSettings settings = ZoneManager.getSettings(getName());
		if (settings == null) {
			settings = new Settings();
		}
		setSettings(settings);
	}
	
	public static final class Settings extends AbstractZoneSettings {
		private int _siegableId = -1;
		private Siegable _siege = null;
		private boolean _isActiveSiege = false;
		
		protected Settings() {
		}
		
		public int getSiegeableId() {
			return _siegableId;
		}
		
		protected void setSiegeableId(int id) {
			_siegableId = id;
		}
		
		public Siegable getSiege() {
			return _siege;
		}
		
		public void setSiege(Siegable s) {
			_siege = s;
		}
		
		public boolean isActiveSiege() {
			return _isActiveSiege;
		}
		
		public void setActiveSiege(boolean val) {
			_isActiveSiege = val;
		}
		
		@Override
		public void clear() {
			_siegableId = -1;
			_siege = null;
			_isActiveSiege = false;
		}
	}
	
	@Override
	public Settings getSettings() {
		return (Settings) super.getSettings();
	}
	
	@Override
	public void setParameter(String name, String value) {
		switch (name) {
			case "castleId", "fortId" -> {
				if (getSettings().getSiegeableId() != -1) {
					throw new IllegalArgumentException("Siege object already defined!");
				}
				getSettings().setSiegeableId(Integer.parseInt(value));
			}
			case "clanHallId" -> {
				if (getSettings().getSiegeableId() != -1) {
					throw new IllegalArgumentException("Siege object already defined!");
				}
				getSettings().setSiegeableId(Integer.parseInt(value));
				SiegableHall hall = ClanHallSiegeManager.getInstance().getConquerableHalls().get(getSettings().getSiegeableId());
				if (hall == null) {
					_log.warning("L2SiegeZone: Siegable clan hall with id " + value + " does not exist!");
				} else {
					hall.setSiegeZone(this);
				}
			}
			default -> super.setParameter(name, value);
		}
	}
	
	@Override
	protected void onEnter(L2Character character) {
		if (getSettings().isActiveSiege()) {
			character.setInsideZone(ZoneId.PVP, true);
			character.setInsideZone(ZoneId.SIEGE, true);
			character.setInsideZone(ZoneId.NO_SUMMON_FRIEND, true); // FIXME: Custom ?
			
			if (character.isPlayer()) {
				L2PcInstance player = character.getActingPlayer();
				if (player.isRegisteredOnThisSiegeField(getSettings().getSiegeableId())) {
					player.setIsInSiege(true); // in siege
					if (getSettings().getSiege().giveFame() && (getSettings().getSiege().getFameFrequency() > 0)) {
						player.startFameTask(getSettings().getSiege().getFameFrequency(), getSettings().getSiege().getFameAmount());
					}
				}
				
				character.sendPacket(SystemMessageId.ENTERED_COMBAT_ZONE);
				if (!castle().allowRideWyvernDuringSiege() && (player.getMountType() == MountType.WYVERN)) {
					player.sendPacket(SystemMessageId.AREA_CANNOT_BE_ENTERED_WHILE_MOUNTED_WYVERN);
					player.enteredNoLanding(DISMOUNT_DELAY);
				}
			}
		}
	}
	
	@Override
	protected void onExit(L2Character character) {
		character.setInsideZone(ZoneId.PVP, false);
		character.setInsideZone(ZoneId.SIEGE, false);
		character.setInsideZone(ZoneId.NO_SUMMON_FRIEND, false); // FIXME: Custom ?
		if (getSettings().isActiveSiege()) {
			if (character.isPlayer()) {
				L2PcInstance player = character.getActingPlayer();
				character.sendPacket(SystemMessageId.LEFT_COMBAT_ZONE);
				if (player.getMountType() == MountType.WYVERN) {
					player.exitedNoLanding();
				}
				// Set pvp flag
				if (player.getPvpFlag() == 0) {
					player.startPvPFlag();
				}
			}
		}
		if (character.isPlayer()) {
			L2PcInstance activeChar = character.getActingPlayer();
			activeChar.stopFameTask();
			activeChar.setIsInSiege(false);
			
			if ((getSettings().getSiege() instanceof FortSiege) && (activeChar.getInventory().getItemByItemId(9819) != null)) {
				// drop combat flag
				Fort fort = FortManager.getInstance().getFortById(getSettings().getSiegeableId());
				if (fort != null) {
					FortSiegeManager.getInstance().dropCombatFlag(activeChar, fort.getResidenceId());
				} else {
					int slot = activeChar.getInventory().getSlotFromItem(activeChar.getInventory().getItemByItemId(9819));
					activeChar.getInventory().unEquipItemInBodySlot(slot);
					activeChar.destroyItem("CombatFlag", activeChar.getInventory().getItemByItemId(9819), null, true);
				}
			}
		}
	}
	
	@Override
	public void onDieInside(L2Character character) {
		if (getSettings().isActiveSiege()) {
			// debuff participants only if they die inside siege zone
			if (character.isPlayer() && character.getActingPlayer().isRegisteredOnThisSiegeField(getSettings().getSiegeableId())) {
				int lvl = 1;
				final BuffInfo info = character.getEffectList().getBuffInfoBySkillId(5660);
				if (info != null) {
					lvl = Math.min(lvl + info.getSkill().getLevel(), 5);
				}
				
				final Skill skill = SkillData.getInstance().getSkill(5660, lvl);
				if (skill != null) {
					skill.applyEffects(character, character);
				}
			}
		}
	}
	
	public void updateZoneStatusForCharactersInside() {
		if (getSettings().isActiveSiege()) {
			for (L2Character character : getCharactersInside()) {
				if (character != null) {
					onEnter(character);
				}
			}
		} else {
			L2PcInstance player;
			for (L2Character character : getCharactersInside()) {
				if (character == null) {
					continue;
				}
				
				character.setInsideZone(ZoneId.PVP, false);
				character.setInsideZone(ZoneId.SIEGE, false);
				character.setInsideZone(ZoneId.NO_SUMMON_FRIEND, false);
				
				if (character.isPlayer()) {
					player = character.getActingPlayer();
					character.sendPacket(SystemMessageId.LEFT_COMBAT_ZONE);
					player.stopFameTask();
					if (player.getMountType() == MountType.WYVERN) {
						player.exitedNoLanding();
					}
				}
			}
		}
	}
	
	/**
	 * Sends a message to all players in this zone
	 * @param message
	 */
	public void announceToPlayers(String message) {
		for (L2PcInstance player : getPlayersInside()) {
			if (player != null) {
				player.sendMessage(message);
			}
		}
	}
	
	public int getSiegeObjectId() {
		return getSettings().getSiegeableId();
	}
	
	public boolean isActive() {
		return getSettings().isActiveSiege();
	}
	
	public void setIsActive(boolean val) {
		getSettings().setActiveSiege(val);
	}
	
	public void setSiegeInstance(Siegable siege) {
		getSettings().setSiege(siege);
	}
	
	/**
	 * Removes all foreigners from the zone
	 * @param owningClanId
	 */
	public void banishForeigners(int owningClanId) {
		TeleportWhereType type = TeleportWhereType.TOWN;
		for (L2PcInstance temp : getPlayersInside()) {
			if (temp.getClanId() == owningClanId) {
				continue;
			}
			
			temp.teleToLocation(type);
		}
	}
}
